package com.smartfast4j.api.common.interceptor;

import com.alibaba.fastjson.JSON;
import com.smartfast4j.api.common.bean.CommonParam;
import com.smartfast4j.common.bean.R;
import com.smartfast4j.common.util.MD5Util;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
import java.text.MessageFormat;

/**
 * Api拦截器
 */
@Component
public class ApiInterceptor extends HandlerInterceptorAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(ApiInterceptor.class);

    @Value("${comm.param.validate}")
    private boolean validate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        LOGGER.debug(">>>ApiInterceptor>>>>>>>在请求处理之前进行调用（Controller方法调用之前）");
        if(!validate){
            LOGGER.debug("----已经关闭公共参数校验----");
            return super.preHandle(request, response, handler);// 只有返回true才会继续向下执行，返回false取消当前请求
        }
        LOGGER.debug("----公共参数校验----");
        String clientType = request.getParameter("clientType");
        String versionCode = request.getParameter("versionCode");
        String versionName = request.getParameter("versionName");
        String sign = request.getParameter("sign");
        String deviceId = request.getParameter("deviceId");
        String timestamp = request.getParameter("timestamp");
        //公共请求参数
        CommonParam commonParam = new CommonParam();
        commonParam.setClientType(clientType);//客户端类型
        commonParam.setVersionCode(versionCode);
        commonParam.setVersionName(versionName);
        commonParam.setSign(sign);
        commonParam.setDeviceId(deviceId);
        commonParam.setTimestamp(timestamp);
        //校验公共请求参数
        String errMsg = validate(commonParam);
        if(StringUtils.isNotEmpty(errMsg)){//公共请求参数校验不通过
            write(JSON.toJSONString(R.fail(errMsg)),response);
            return false;//返回false取消当前请求，不再往下执行
        }else {
            return super.preHandle(request, response, handler);// 只有返回true才会继续向下执行，返回false取消当前请求
        }
    }

    /**
     * 写出数据
     *
     * @param res 输出的字符串
     * @throws Exception
     */
    protected void write(String res, HttpServletResponse response) {
        Writer writer = null;
        try {
            res = (null == res ? "" : res);
            response.setCharacterEncoding("UTF-8");
            //response.setHeader("Content-type", "text/html;charset=UTF-8");
            response.setHeader("Content-type", "application/json;charset=UTF-8");
            writer = response.getWriter();
            LOGGER.debug("输出JSON字符串：" + res);
            writer.write(res);
        } catch (IOException e) {
            LOGGER.error("输出JSON字符串异常", e);
        } finally {
            IOUtils.closeQuietly(writer);
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        LOGGER.debug(">>>ApiInterceptor>>>>>>>请求处理之后进行调用，但是在视图被渲染之前（Controller方法调用之后）");
        super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        LOGGER.debug(">>>ApiInterceptor>>>>>>>在整个请求结束之后被调用，也就是在DispatcherServlet 渲染了对应的视图之后执行（主要是用于进行资源清理工作）");
    }

    /**
     * 校验公共请求参数
     */
    private String validate(CommonParam commonParam){
        String errMsg = "";
        //1.校验公共参数必填项
        if(StringUtils.isEmpty(commonParam.getClientType())){
            errMsg = "请求参数错误，缺少客户端类型";
            return errMsg;
        }
        if(StringUtils.isEmpty(commonParam.getVersionCode())){
            errMsg = "请求参数错误，缺少客户端版本号";
            return errMsg;
        }
        if(StringUtils.isEmpty(commonParam.getVersionName())){
            errMsg = "请求参数错误，缺少客户端版名";
            return errMsg;
        }
        if(StringUtils.isEmpty(commonParam.getDeviceId())){
            errMsg = "请求参数错误，缺少设备ID";
            return errMsg;
        }
        if(StringUtils.isEmpty(commonParam.getSign())){
            errMsg = "请求参数错误，缺少签名";
            return errMsg;
        }
        if(StringUtils.isEmpty(commonParam.getTimestamp())){
            errMsg = "请求参数错误，缺少时间戳";
            return errMsg;
        }
        //2.校验签名
        String sign = createSign(commonParam);
        if(!sign.equalsIgnoreCase(commonParam.getSign())){
            errMsg = "请求的签名校验不通过";
            return errMsg;
        }

       return "";
    }

    /**
     * 根据公共请求参数生成签名
     * @param commonParam
     * @return
     */
    private String createSign(CommonParam commonParam){
        //签名生成规则：MD5(clientType&versionCode&versionName&deviceId&timestamp)
        String signRes = MessageFormat.format("{0}&{1}&{2}&{3}&{4}",
                commonParam.getClientType(),
                commonParam.getVersionCode(),
                commonParam.getVersionName(),
                commonParam.getDeviceId(),
                commonParam.getTimestamp());
        LOGGER.debug("用于生成签名的字符串："+signRes);
        String sign = MD5Util.getStringMD5(signRes);
        LOGGER.debug("生成的签名："+sign);
        return sign;
    }
}
